using Aragorn.SOA.ORM.Lambda;
using Aragorn.SOA.ORM.Lambda.Adapter;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using Aragorn.SOA.ORM.Lambda.Adapter;
using Aragorn.SOA.ORM.Lambda.ValueObjects;

namespace Aragorn.SOA.ORM.Lambda.Builder
{
	public class SqlQueryBuilder
	{
		private const string PARAMETER_PREFIX = "Param";

		private readonly List<string> _tableNames = new List<string>();

		private readonly List<string> _joinExpressions = new List<string>();

		private readonly List<string> _selectionList = new List<string>();

		private readonly List<string> _conditions = new List<string>();

		private readonly List<string> _sortList = new List<string>();

		private readonly List<string> _groupingList = new List<string>();

		private readonly List<string> _havingConditions = new List<string>();

		private readonly List<string> _splitColumns = new List<string>();

		private int _paramIndex;

		internal ISqlAdapter Adapter
		{
			get;
			set;
		}

		private string Conditions
		{
			get
			{
				if (this._conditions.Count == 0)
				{
					return "";
				}
				return string.Concat("WHERE ", string.Join("", this._conditions));
			}
		}

		public int CurrentParamIndex
		{
			get
			{
				return this._paramIndex;
			}
		}

		public string DeleteString
		{
			get
			{
				return this.Adapter.DeleteString(this.Source, this.Conditions);
			}
		}

		public List<string> GroupByList
		{
			get
			{
				return this._groupingList;
			}
		}

		private string Grouping
		{
			get
			{
				if (this._groupingList.Count == 0)
				{
					return "";
				}
				return string.Concat("GROUP BY ", string.Join(", ", this._groupingList));
			}
		}

		private string Having
		{
			get
			{
				if (this._havingConditions.Count == 0)
				{
					return "";
				}
				return string.Concat("HAVING ", string.Join(" ", this._havingConditions));
			}
		}

		public List<string> HavingConditions
		{
			get
			{
				return this._havingConditions;
			}
		}

		public List<string> JoinExpressions
		{
			get
			{
				return this._joinExpressions;
			}
		}

		private string Order
		{
			get
			{
				if (this._sortList.Count == 0)
				{
					return "";
				}
				return string.Concat("ORDER BY ", string.Join(", ", this._sortList));
			}
		}

		public List<string> OrderByList
		{
			get
			{
				return this._sortList;
			}
		}

		public IDictionary<string, object> Parameters
		{
			get;
			private set;
		}

		public string QueryString
		{
			get
			{
				return this.Adapter.QueryString(this.Selection, this.Source, this.Conditions, this.Grouping, this.Having, this.Order);
			}
		}

		private string Selection
		{
			get
			{
				if (this._selectionList.Count != 0)
				{
					return string.Join(", ", this._selectionList);
				}
				return string.Format("{0}.*", this.Adapter.Table(this._tableNames.First<string>()));
			}
		}

		public List<string> SelectionList
		{
			get
			{
				return this._selectionList;
			}
		}

		private string Source
		{
			get
			{
				string str = string.Join(" ", this._joinExpressions);
				return string.Format("{0} {1}", this.Adapter.Table(this._tableNames.First<string>()), str);
			}
		}

		public List<string> SplitColumns
		{
			get
			{
				return this._splitColumns;
			}
		}

		public List<string> TableNames
		{
			get
			{
				return this._tableNames;
			}
		}

		public List<string> WhereConditions
		{
			get
			{
				return this._conditions;
			}
		}

		internal SqlQueryBuilder(string tableName, ISqlAdapter adapter)
		{
			this._tableNames.Add(tableName);
			this.Adapter = adapter;
			this.Parameters = new ExpandoObject();
			this._paramIndex = 0;
		}

		private void AddParameter(string key, object value)
		{
			if (!this.Parameters.ContainsKey(key))
			{
				this.Parameters.Add(key, value);
			}
		}

		public void And()
		{
			if (this._conditions.Count > 0)
			{
				this._conditions.Add(" AND ");
			}
		}

		public void BeginExpression()
		{
			this._conditions.Add("(");
		}

		public void EndExpression()
		{
			this._conditions.Add(")");
		}

		public void GroupBy(string tableName, string fieldName)
		{
			this._groupingList.Add(this.Adapter.Field(tableName, fieldName));
		}

		public void Join(string originalTableName, string joinTableName, string leftField, string rightField)
		{
			string str = string.Format("JOIN {0} ON {1} = {2}", this.Adapter.Table(joinTableName), this.Adapter.Field(originalTableName, leftField), this.Adapter.Field(joinTableName, rightField));
			this._tableNames.Add(joinTableName);
			this._joinExpressions.Add(str);
			this._splitColumns.Add(rightField);
		}

		private string NextParamId()
		{
			SqlQueryBuilder sqlQueryBuilder = this;
			sqlQueryBuilder._paramIndex = sqlQueryBuilder._paramIndex + 1;
			return string.Concat("Param", this._paramIndex.ToString(CultureInfo.InvariantCulture));
		}

		public void Not()
		{
			this._conditions.Add(" NOT ");
		}

		public void Or()
		{
			if (this._conditions.Count > 0)
			{
				this._conditions.Add(" OR ");
			}
		}

		public void OrderBy(string tableName, string fieldName, bool desc = false)
		{
			string str = this.Adapter.Field(tableName, fieldName);
			if (desc)
			{
				str = string.Concat(str, " DESC");
			}
			this._sortList.Add(str);
		}

		public void QueryByField(string tableName, string fieldName, string op, object fieldValue)
		{
			string str = this.NextParamId();
			string str1 = string.Format("{0} {1} {2}", this.Adapter.Field(tableName, fieldName), op, this.Adapter.Parameter(str));
			this._conditions.Add(str1);
			this.AddParameter(str, fieldValue);
		}

		public void QueryByFieldComparison(string leftTableName, string leftFieldName, string op, string rightTableName, string rightFieldName)
		{
			string str = string.Format("{0} {1} {2}", this.Adapter.Field(leftTableName, leftFieldName), op, this.Adapter.Field(rightTableName, rightFieldName));
			this._conditions.Add(str);
		}

		public void QueryByFieldLike(string tableName, string fieldName, string fieldValue)
		{
			string str = this.NextParamId();
			string str1 = string.Format("{0} LIKE {1}", this.Adapter.Field(tableName, fieldName), this.Adapter.Parameter(str));
			this._conditions.Add(str1);
			this.AddParameter(str, fieldValue);
		}

		public void QueryByFieldNotNull(string tableName, string fieldName)
		{
			this._conditions.Add(string.Format("{0} IS NOT NULL", this.Adapter.Field(tableName, fieldName)));
		}

		public void QueryByFieldNull(string tableName, string fieldName)
		{
			this._conditions.Add(string.Format("{0} IS NULL", this.Adapter.Field(tableName, fieldName)));
		}

		public void QueryByIsIn(string tableName, string fieldName, SqlLamBase sqlQuery)
		{
			string queryString = sqlQuery.QueryString;
			foreach (KeyValuePair<string, object> queryParameter in sqlQuery.QueryParameters)
			{
				string str = string.Concat("Inner", queryParameter.Key);
				queryString = Regex.Replace(queryString, queryParameter.Key, str);
				this.AddParameter(str, queryParameter.Value);
			}
			string str1 = string.Format("{0} IN ({1})", this.Adapter.Field(tableName, fieldName), queryString);
			this._conditions.Add(str1);
		}

		public void QueryByIsIn(string tableName, string fieldName, IEnumerable<object> values)
		{
			IEnumerable<string> strs = values.Select<object, string>((object x) => {
				string str = this.NextParamId();
				this.AddParameter(str, x);
				return this.Adapter.Parameter(str);
			});
			string str1 = string.Format("{0} IN ({1})", this.Adapter.Field(tableName, fieldName), string.Join(",", strs));
			this._conditions.Add(str1);
		}

		public string QueryStringPage(int pageSize, int? pageNumber = null)
		{
			if (!pageNumber.HasValue)
			{
				return this.Adapter.QueryStringPage(this.Source, this.Selection, this.Conditions, this.Order, pageSize);
			}
			if (this._sortList.Count == 0)
			{
				throw new Exception("Pagination requires the ORDER BY statement to be specified");
			}
			return this.Adapter.QueryStringPage(this.Source, this.Selection, this.Conditions, this.Order, pageSize, pageNumber.Value);
		}

		public void Select(string tableName)
		{
			string str = string.Format("{0}.*", this.Adapter.Table(tableName));
			this._selectionList.Add(str);
		}

		public void Select(string tableName, string fieldName)
		{
			this._selectionList.Add(this.Adapter.Field(tableName, fieldName));
		}

		public void Select(string tableName, string fieldName, SelectFunction selectFunction)
		{
			string str = string.Format("{0}({1})", selectFunction.ToString(), this.Adapter.Field(tableName, fieldName));
			this._selectionList.Add(str);
		}
	}
}